#include "../include/scheduler.h"
#include "../include/sleep.h"
#include "../include/kasm.h"
#include "../include/kc.h"

extern process_t processes[];	//lista de procesos
extern int currProc;			//proceso actual

//estructura de un proceso que se encuentra dormido
typedef struct {
	int procPid;
	int sleepTime;
	int next;
} asleepProcces_t;
//vector de procesos durmiendo
static asleepProcces_t asleepProcesses[MAX_CANT_PROC];

#define TICS_PER_SECOND 18
#define MAX_ASLEEP_TIME 300

static int sleepCant = 0;

static int getAvailableIdx();
static int firstElemIdx = 0;

int
sleepImpl(int segundos) {
	if (segundos <= 0)
		return 0;

	if (segundos > MAX_ASLEEP_TIME || sleepCant >= MAX_CANT_PROC)
		return -1;

	processes[currProc].state = BLOCKED;
	processes[currProc].block = B_ASLEEP;

	int sleepTime = segundos * TICS_PER_SECOND;
	int i, next = firstElemIdx, prev = firstElemIdx, insertFlag = 0;

	for (i = 0; i < sleepCant && next != -1 && !insertFlag; i++) {
		if (sleepTime - asleepProcesses[next].sleepTime >= 0) {
			//si la cuenta me da positiva, resto el tiempo y sigo ciclando
			sleepTime -= asleepProcesses[next].sleepTime;
			prev = next;
			next = asleepProcesses[next].next;
		}
		else {
			//si la cuenta da negativa, tengo que insertar el proceso en el
			//medio de la lista, asi que le resto la diferencia al siguiente y
			//salgo del ciclo, encendiendo el flag de insert
			asleepProcesses[next].sleepTime -= sleepTime;
			insertFlag = 1;
		}
	}

	int idx = getAvailableIdx();
	asleepProcesses[idx].procPid = processes[currProc].pid;
	asleepProcesses[idx].sleepTime = sleepTime;
	asleepProcesses[idx].next = insertFlag? next : -1;

	//si sali en el primer ciclo, y con el flag de insert, es que soy el
	//nuevo primer elemento
	if (insertFlag == 1 && i == 1) {
		firstElemIdx = idx;
	}
	else {
		//si no soy el 1ro, hago que el anterior apunte a mi
		if (i != 0)
			asleepProcesses[prev].next = idx;
	}

	sleepCant++;

	_sys_yield();

	return 0;
}

void
updateSleepingProcesses(void) {
	process_t * proc;

	if (sleepCant == 0)
		return;

	asleepProcesses[firstElemIdx].sleepTime--;
	//si es hora de despertar al primer proceso, lo despierto y ademas me
	//fijo uno o mas de los procesos que le siguen inmediatamente deben
	//ser despertados tambien
	int idx = firstElemIdx, aux;
	while (idx != -1 && asleepProcesses[idx].sleepTime == 0) {
		proc = getProcByPid(asleepProcesses[idx].procPid);
		if (proc != NULL) {
			proc->state = READY;
		}
		sleepCant--;
		aux = asleepProcesses[idx].next;//salvo el idx del prox proceso
		asleepProcesses[idx].next = -1; //doy de baja el proceso
		asleepProcesses[idx].procPid  = -1;
		idx = aux;
	}
	//si idx es -1, me quede sin procesos, asi que le asigno 0 al firstIdx
	firstElemIdx = (idx == -1)? 0 : idx;
}

void
initSleep(void) {
	int i;
	for (i = 0; i < MAX_CANT_PROC; i++) {
		asleepProcesses[i].next  = -1;
		asleepProcesses[i].procPid  = -1;
	}
}

static int
getAvailableIdx(void) {
	int i;
	for (i = 0; i < MAX_CANT_PROC; i++)
		if (asleepProcesses[i].procPid == -1)
			return i;

	//no deberia salir por aca
	return 0;
}
